module UnitSize

import IO;
import List;
import String;
import Volume;
import lang::java::jdt::JavaADT;
import lang::java::jdt::Java;
import lang::java::jdt::JDT;
import util::Math;

// loc is in format 
// |project://<filename>.java|(O,L,<BL,BC>,<EL,EC>)
// O      = offset of this location to the begin of the file
// L      = length of the file
// BL, BC = beginline and begincolumn of this location
// EL, EC = endline and endcolumn of this location
public int unitSize(loc unit)
{	
	str _unit = readFile(unit);
	if (/.*abstract.*/ := _unit)
	{
		// Abstract method declaration is implemented elsewhere.
		return 0;
	}
	return countLines(remExtras(_unit));
}

public real unitSizeClass(list[loc] locs)
{
	str ranking = getRanking(locs);
	
	switch(ranking) {
		case "--" : return 1.0;
		case "-"  : return 0.8;
		case "o"  : return 0.6;
		case "+"  : return 0.4;
		case "++" : return 0.2;
	}
}

public str getRanking()
{
	return getRanking([]);
}

public str getRanking(loc file)
{
	return getRanking([file]);
}

public str getRanking(list[loc] files)
{
	BindingRel methods = {};
	if(files == [])
	{
		fm = extractProject(|project://SmallSql|);
		//fm = extractProject(|project://hsqldb|);
		methods = fm@methodDecls;
	}
	else
	{
		for(loc file <- files)
		{
			fm = extractClass(file);
			//fm = extractClass(|project://SmallSql/src/smallsql/database/Logger.java|);
			methods += fm@methodDecls;
		}
	}
	
	
	counts = [];
	visit(methods) {
		case <loc l, Entity e> : { counts = counts + [unitSize(l)]; }
		//case <loc l, Entity e>: { iprintln(e); iprintln(l); }
	}
	
	mp = ("without" : 0, "moderate" : 0, "high" : 0, "very high" : 0, "all" : 0);
	
	for(int i <- counts)
	{
		switch(getRisk(i)) 	{ 	case "without":   { mp["without"]   += i; mp["all"] += i; }
								case "moderate":  { mp["moderate"]  += i; mp["all"] += i; }
								case "high":      { mp["high"]      += i; mp["all"] += i; }
								case "very high": { mp["very high"] += i; mp["all"] += i; }
							};
	}
	
	//println(mp);
	
 	real without,moderate,high,veryhigh;
 	if(mp["all"] == 0)
	{
		return "++";		
	}
 	without  = toReal(mp["without"])   / toReal(mp["all"]);
 	moderate = toReal(mp["moderate"])  / toReal(mp["all"]);
 	high     = toReal(mp["high"])      / toReal(mp["all"]);
 	veryhigh = toReal(mp["very high"]) / toReal(mp["all"]);
 	
 	real average = toReal(mp["all"]) / toReal(size(counts));
 	//println("average:   " + toString(average));
 	//
 	//println("without:   " + toString(without));
 	//println("moderate:  " + toString(moderate));
 	//println("high:      " + toString(high));
 	//println("very high: " + toString(veryhigh));
 	
 	if (moderate <= 0.25 && high <= 0.0 && veryhigh <= 0.0)
 		return "++";
 	else if (moderate <= 0.30 && high <= 0.05 && veryhigh <= 0.0)
 		return "+";
 	else if (moderate <= 0.40 && high <= 0.10 && veryhigh <= 0.0)
 		return "o";
 	else if (moderate < 0.50 && high < 0.15 && veryhigh < 0.05)
 		return "-";
 	else
 		return "--";
}

// Convert lines of code to risk factor.
public str getRisk(int count)
{
	if(count <= 20) 
		return "without";
	else if(count <= 50)
		return "moderate"; 
	else if(count <= 100)
		return "high";
	else
		return "very high";
}